/*************************************************************************
	> File Name: 8.3.1Sychronized--条件变量.c
	> Author: Potter
	> Mail: tobewhatwewant@gmail.com
	> Created Time: 2014年09月16日 星期二 11时53分16秒
 ************************************************************************/

/*
* 条件变量的结构为 pthread_cond_t
* 函数 pthread_cond_init() 被用来初始化一个条件变量.
*   int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr);
*  其中 cond 是一个指向结构 pthread_cond_t 的指针，cond_attr 
*  是一个指向结构  pthread_condattr_t 的指针。结构 pthread_condattr_t 是条件
*  变量的属性结构，和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进
*  程间可用，默认值是 PTHREAD_PROCESS_PRIVATE，即此条件被同一进程内的各个
*  线程使用.注意初始化条件变量只有未被使用时才能重新初始化或被释放。条件变量
*  也可用宏定义PTHREAD_COND_INITIALIZE 初始化，相当于用 pthread_cond_init
*  函数初始化并且 attr 参数为 NULL;
*       pthread_cond_t cond = PTHREAD_COND_INITIALIZE;
*     释放一个条件变量的函数为 pthread_cond_destroy(pthread_cond_t * cond)
*     函数 pthread_cond_wait() 使线程阻塞在一个条件变量上。
*       int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
*     线程解开 mutex 指向的锁并被条件变量 cond 阻塞。线程可以被函数
*     pthread_cond_signal() 和 函数 pthread_cond_broadcast 唤醒，但是要
*     注意的是，条件变量只是起阻塞和唤醒线程的作用，具体的判断条件还需
*     用户给出，例如一个变量是否为0等等。线程被唤醒后，它见重新检查判断
*     提哦案件是否满足，如果不满组，一般来说线程仍阻塞在这里，
*     等待下一次唤醒。这个过程一般用 while(语句实现)。另一个用来阻塞线程的
*     函数是 pthread_cond_timewait():
*       int pthread_cond_timewait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * abstime);
*       它比函数 pthread_cond_wait()多用了一个时间参数，经历 abstime 段
*     时间后，即使条件变量不满足，阻塞也被解除;
*       int pthread_cond_signal(pthread_cond_t * cond)
*     它用来释放被阻塞在条件变量cond上的一个线程。多个线程在此条件变量上
*     时，哪个线程被唤醒是有线程调度策略所决定的。要注意的是，必须用保护
*     条件变量的互斥锁来保护这个函数，否则条件满足信号又可能在测试条件和
*     调用 pthread_cond_wait函数之间被发出，从而造成无限制的等待.
* */

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

struct msg {
    struct msg * next;
    int num;
};

struct msg * head;
// 相当于 pthread_cond_t has_product;
//      pthread_cond_init(&has_product, NULL);
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
// 相当于 pthread_mutex_t lock;
//      pthread_mutex_init(&lock, NULL);
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

// 生产者的线程代码
void * producer(void * p) {
    struct msg * mp;
    int i;
    for(i=0; i<20; i++) {
        mp = (struct msg *)malloc(sizeof(struct mgs *));
        mp->num = rand() % 1000 + 1;
        fprintf(stdout, "Produce %d\n", mp->num);

        pthread_mutex_lock(&lock);
        mp->next = head;
        // @TODO ???
        head = mp;
        pthread_mutex_unlock(&lock);

        pthread_cond_signal(&has_product); // 唤醒消费者线程
        sleep(rand() % 5);
    }
    return NULL;
}  

// 消费者代码
void * consumer(void * p) {
    struct msg * mp;
    int i;
    for (i=0; i<20; ++i) {
        
        pthread_mutex_lock(&lock);
        while(head == NULL)
            pthread_cond_wait(&has_product, &lock); // 没有数据(消息)，阻塞消费者线程，解锁
        mp = head;
        head = mp->next;
        pthread_mutex_unlock(&lock);

        fprintf(stdout, "Consumer %d\n", mp->num);
        free(mp);
        sleep(rand() % 5);
    }
    return NULL;
}

int main(int argc, char * argv[]) {
    pthread_t pt, ct;

    srand(time(NULL));
    pthread_create(&pt, NULL, producer, NULL);
    pthread_create(&ct, NULL, consumer, NULL);

    pthread_join(pt, NULL);
    pthread_join(ct, NULL);

    return 0;
}
